package io.xxx.xbutler.core.callback;

import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.xxx.xbutler.constant.Formats;
import io.xxx.xbutler.constant.Topics;
import io.xxx.xbutler.data.CallbackMapper;
import io.xxx.xbutler.domain.Callback;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.pulsar.annotation.PulsarListener;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

import java.time.LocalDateTime;
import java.util.Map;

/**
 * 探鲸平台回调处理调度
 */
@Slf4j
@Component

public class CallbackHandleListener {

    @Resource
    private CallbackMapper callbackMapper;

    @Resource
    private Map<String, CallbackHandler> callbackHandlers;

    @PulsarListener(topics = Topics.CALLBACK)
    public void received(Long id) {
        StopWatch watch = new StopWatch();
        watch.start();

        processing(id);

        Callback callback = callbackMapper.selectById(id);
        if (callback == null) {
            log.error("回调信息不存在[{}]", id);
            return;
        }
        Integer callbackType = callback.getType();
        String serialNo = callback.getSerialNo();
        String robotId = callback.getRobotId();
        String robotSerialNo = callback.getRobotSerialNo();
        log.debug("[{}]回调处理开始[id:{}, serialNo:{}, robotId:{}, robotSerialNo:{}]",
                callbackType, id, serialNo, robotId, robotSerialNo);

        Exception ex = null;
        CallbackHandler supportsHandler = null;
        String handlerName = null;
        Integer type = callback.getType();
        for (Map.Entry<String, CallbackHandler> entry : callbackHandlers.entrySet()) {  // handlerName - handlerBean
            CallbackHandler handler = entry.getValue();

            if (handler.supports(type)) {
                supportsHandler = handler;
                handlerName = supportsHandler.name();
                try {
                    if (handler.ignoreError() && !callback.isSuccess()) {
                        LambdaUpdateWrapper<Callback> wrapper = new LambdaUpdateWrapper<Callback>()
                                .set(Callback::getStatus, 4)
                                .eq(Callback::getId, id);
                        callbackMapper.update(wrapper);
                        log.debug("[{}]{}处理忽略[id:{}, serialNo:{}, code:{}, reason:{}]", callbackType, handlerName, id, serialNo, callback.getCode(), callback.getMessage());
                        return;
                    }
                    handler.handle(callback);
                } catch (Exception e) {
                    log.error(e.getMessage(), e);
                    ex = e;
                }
                break;  // 保证只有一个处理器被应用
            }
        }

        if (watch.isRunning()) {
            watch.stop();
        }
        String spentTime = Formats.NUMBER_FORMAT.format(watch.getTotalTimeSeconds());
        if (supportsHandler == null) {
            log.info("[{}]回调未找到处理器({}毫秒)[id:{}, serialNo:{}]",
                    callbackType, spentTime, id, serialNo);
        } else {
            log.debug("[{}]{}处理完成({}毫秒)[id:{}, serialNo:{}, robotId:{}, robotSerialNo:{}]",
                    callbackType, handlerName, spentTime, id, serialNo, robotId, robotSerialNo);
        }

        processed(id, ex);
    }

    private void processing(Long id) {
        LambdaUpdateWrapper<Callback> wrapper = new LambdaUpdateWrapper<Callback>()
                .set(Callback::getStatus, 1)
                .set(Callback::getUpdatedTime, LocalDateTime.now())
                .eq(Callback::getId, id);
        callbackMapper.update(wrapper);
    }

    private void processed(Long id, Exception e) {
        LambdaUpdateWrapper<Callback> wrapper = new LambdaUpdateWrapper<Callback>()
                .set(Callback::getStatus, e == null ? 2 : 3)
                .set(Callback::getUpdatedTime, LocalDateTime.now())
                .eq(Callback::getId, id);
        if (e != null) {
            wrapper.set(Callback::getException, e.getLocalizedMessage());
        } else {
            wrapper.set(Callback::getException, null);
        }
        callbackMapper.update(wrapper);
    }
}
